package me.wz.util.sdbc.dbutils;

import me.wz.util.exception.RTException;
import me.wz.util.sdbc.ITransaction;
import org.apache.commons.dbutils.DbUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.Callable;

public abstract class TranscationSupport implements ITransaction {

    ThreadLocal<Connection> TLConnection = new ThreadLocal<Connection>();
    ThreadLocal<Boolean> TLAutoCommit = new ThreadLocal<Boolean>();

    public Connection getTransactionConnection(){ return TLConnection.get(); }

    public void startTransaction() {
        try {
            Connection connection = getConnection();
            TLConnection.set(connection);
            TLAutoCommit.set(connection.getAutoCommit());
            connection.setAutoCommit(false);
        }
        catch(SQLException e) {
            throw new RTException(e.getMessage(), e);
        }
    }
    public void commit() {
        try {
            if( TLConnection.get() != null ){
                TLConnection.get().commit();
            }
        }
        catch(SQLException e) {
            throw new RTException(e.getMessage(), e);
        }
    }
    public void rollback() {
        try {
            if( TLConnection.get() != null ){
                TLConnection.get().rollback();
            }
        }
        catch(SQLException e) {
            throw new RTException(e.getMessage(), e);
        }
    }
    public void close() {
        try {
            if( TLConnection.get() != null ){
                if( TLAutoCommit.get() != null ) {
                    TLConnection.get().setAutoCommit(TLAutoCommit.get());
                }
            }
        }
        catch(SQLException e) {
            throw new RTException(e.getMessage(), e);
        }
        finally {
            TLConnection.remove();
            TLAutoCommit.remove();
            DbUtils.closeQuietly(TLConnection.get());
        }
    }

    public <T>T transaction(Callable<T> callable) {
        try {
            return transaction0(callable);
        }
        catch (Exception e) {
            throw new RTException(e.getMessage(), e);
        }
    }
    private <T>T transaction0(Callable<T> callable) throws Exception {
        try {
            startTransaction();
            T ret = callable.call();
            commit();
            return ret;
        }
        catch(Exception e) {
            rollback();
            throw e;
        }
        finally {
            close();
        }
    }

    protected abstract Connection getConnection() throws SQLException;
}
